﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;


namespace GomokuServer
{
    public partial class Form1 : Form
    {
        List<Socket> NumOfPlayers = new List<Socket>();

        //The main socket on which the server listens to the clients
        Socket serverSocket;

        byte[] byteData = new byte[1024];
        private const int size = 15;
        int[,] matriz = new int[size, size];


        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                //Criando o socket TCP para o servidor
                serverSocket = new Socket(AddressFamily.InterNetwork,
                                          SocketType.Stream,
                                          ProtocolType.Tcp);

                //Aceitaremos conexões de qualquer IP na porta 1000
                IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 999);

                //Inicia o socket e aguarda (escuta) por conexões
                serverSocket.Bind(ipEndPoint);
                serverSocket.Listen(4);

                //As solicitações de conexão são tratadas de modo assincrono
                //na função AlguemQuerJogar
                serverSocket.BeginAccept(new AsyncCallback(AlguemQuerJogar), null);
                textBox1.Text += "Servidor inicializado..." + Environment.NewLine;
                textBox1.Text += "Aguardando Jogadores..." + Environment.NewLine;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Servidor Gomoku",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void AlguemQuerJogar(IAsyncResult ar)
        {
            try
            {
                Socket clientSocket = serverSocket.EndAccept(ar);
                //Continua aguardando por outros jogadores
                serverSocket.BeginAccept(new AsyncCallback(AlguemQuerJogar), null);

                //As mensagens recebidas do cliente serão direcionados para a função 
                //"Mensagem Recebida"
                clientSocket.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None,
                    new AsyncCallback(MensagemRecebida), clientSocket);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Servidor Gomoku",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void OnSend(IAsyncResult ar)
        {
            try
            {
                Socket client = (Socket)ar.AsyncState;
                client.EndSend(ar);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Servidor Gomoku", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        ProtocolMessage msgToSend = new ProtocolMessage();

        private void MensagemRecebida(IAsyncResult ar)
        {
            try
            {
                Socket clientSocket = (Socket)ar.AsyncState;
                clientSocket.EndReceive(ar);
                ProtocolMessage msgReceived = new ProtocolMessage(byteData);
                msgToSend.Cmd = ProtocolMessage.Command.Null;

                switch (msgReceived.Cmd)
                {
                    case ProtocolMessage.Command.InsertPiece:
                        if (NumOfPlayers.Count == 2)
                        {
                            if (msgToSend.BoardData[(msgReceived.YCoordinate - 1) * 15 + (msgReceived.XCoordinate - 1)] == 0)
                            {
                                this.Invoke((MethodInvoker)delegate()
                                {
                                    textBox1.Text += "Jogador " + msgReceived.SenderID + " inseriu uma peça na posição: " + msgReceived.XCoordinate + ", " + msgReceived.YCoordinate + Environment.NewLine;
                                });
                                int turn = 0;
                                matriz[msgReceived.YCoordinate, msgReceived.XCoordinate] = msgReceived.SenderID;
                                msgToSend.BoardData[(msgReceived.YCoordinate - 1) * 15 + (msgReceived.XCoordinate - 1)] = msgReceived.SenderID;
                                msgToSend.Cmd = ProtocolMessage.Command.RefreshBoard;
                                byte[] message = msgToSend.ToByte();

                                for (int i = 0; i < NumOfPlayers.Count; i++)
                                {
                                    NumOfPlayers[i].BeginSend(message, 0, message.Length, SocketFlags.None,
                                                       new AsyncCallback(OnSend), NumOfPlayers[i]);
                                }
                                //Se foi o jogador 1 que jogou, passa a vez para o 2
                                if (msgReceived.SenderID == 1)
                                {
                                    turn = 1;
                                }
                                ProtocolMessage.WinInform wi;
                                wi = ganhou(msgReceived.SenderID); 
                                if ( wi.direction == ProtocolMessage.WinDirection.Lost)
                                {
                                    msgToSend.Cmd = ProtocolMessage.Command.ItsYourTurn;
                                    msgToSend.MyID = (byte)NumOfPlayers.Count;
                                    message = msgToSend.ToByte();
                                    NumOfPlayers[turn].BeginSend(message, 0, message.Length, SocketFlags.None, new AsyncCallback(OnSend), NumOfPlayers[turn]);
                                }
                                else
                                {
                                    msgToSend.Cmd = ProtocolMessage.Command.EndOfGame;
                                    msgToSend.MatchResult = 1;
                                    msgToSend.MyID = (byte)NumOfPlayers.Count;
                                    msgToSend.WinInformation = wi;
                                    message = msgToSend.ToByte();
                                    clientSocket.BeginSend(message, 0, message.Length, SocketFlags.None, new AsyncCallback(OnSend), clientSocket);

                                    msgToSend.Cmd = ProtocolMessage.Command.EndOfGame;
                                    msgToSend.MatchResult = 0;
                                    msgToSend.MyID = (byte)NumOfPlayers.Count;
                                    message = msgToSend.ToByte();

                                    if (msgReceived.SenderID == 2)
                                        NumOfPlayers[0].BeginSend(message, 0, message.Length, SocketFlags.None, new AsyncCallback(OnSend), NumOfPlayers[0]);
                                    else
                                        NumOfPlayers[1].BeginSend(message, 0, message.Length, SocketFlags.None, new AsyncCallback(OnSend), NumOfPlayers[1]);
                                }
                            }
                            else
                            {
                                this.Invoke((MethodInvoker)delegate()
                                {
                                    textBox1.Text += "Jogador " + msgReceived.SenderID + " tentou inserir em uma posição invalida (" + msgReceived.XCoordinate + ", " + msgReceived.YCoordinate + ")."+ Environment.NewLine;
                                });

                                msgToSend.Cmd = ProtocolMessage.Command.ItsYourTurn;
                                msgToSend.MyID = (byte)NumOfPlayers.Count;
                                byte[] m3 = msgToSend.ToByte();
                                clientSocket.BeginSend(m3, 0, m3.Length, SocketFlags.None, new AsyncCallback(OnSend), clientSocket);
                            }
                        }
                        break;
                    case ProtocolMessage.Command.RefreshBoard:

                        break;
                    case ProtocolMessage.Command.ItsYourTurn:

                        break;
                    case ProtocolMessage.Command.EndOfGame:
                        this.Invoke((MethodInvoker)delegate()
                        {
                            textBox1.Text += "Jogador " + msgReceived.SenderID + " se desconectou" + Environment.NewLine;
                        });

                        msgToSend.Cmd = ProtocolMessage.Command.EndOfGame;
                        msgToSend.MatchResult = 3;
                        msgToSend.MyID = 3;
                        byte[] m2 = msgToSend.ToByte();

                        if (NumOfPlayers.Count == 2)
                        {
                            if (msgReceived.SenderID == 2)
                                NumOfPlayers[0].BeginSend(m2, 0, m2.Length, SocketFlags.None, new AsyncCallback(OnSend), NumOfPlayers[0]);
                            else
                                NumOfPlayers[1].BeginSend(m2, 0, m2.Length, SocketFlags.None, new AsyncCallback(OnSend), NumOfPlayers[1]);
                        }
                        
                        break;
                    case ProtocolMessage.Command.IwantPlay:
                        msgToSend.Cmd = ProtocolMessage.Command.ConnectionResponse;
                        this.Invoke((MethodInvoker)delegate()
                        {
                            textBox1.Text += "Jogador " + ((byte)NumOfPlayers.Count + 1).ToString() + " se conectou." + Environment.NewLine;
                        });
                        if (NumOfPlayers.Count < 2)
                        {
                            NumOfPlayers.Add(clientSocket);
                            msgToSend.ConnStatus = ProtocolMessage.ConnectionStatus.WelcomeAboard;
                            msgToSend.MyID = (byte)NumOfPlayers.Count;
                            byte[] m = msgToSend.ToByte();
                            clientSocket.BeginSend(m, 0, m.Length, SocketFlags.None, new AsyncCallback(OnSend), clientSocket);
                            if (NumOfPlayers.Count == 2)
                            {
                                msgToSend.Cmd = ProtocolMessage.Command.ItsYourTurn;
                                msgToSend.MyID = (byte)NumOfPlayers.Count;
                                m = msgToSend.ToByte();
                                NumOfPlayers[0].BeginSend(m, 0, m.Length, SocketFlags.None, new AsyncCallback(OnSend), NumOfPlayers[0]);


                                msgToSend.Cmd = ProtocolMessage.Command.TextInformation;
                                msgToSend.MyID = (byte)NumOfPlayers.Count;
                                msgToSend.TextMessage = "Aguarde a jogada do seu adversário";
                                m = msgToSend.ToByte();
                                NumOfPlayers[1].BeginSend(m, 0, m.Length, SocketFlags.None, new AsyncCallback(OnSend), NumOfPlayers[1]);

                            }
                            else
                            {
                                msgToSend.Cmd = ProtocolMessage.Command.TextInformation;
                                msgToSend.TextMessage = "Aguarde aparecer um adversário a sua altura";
                                msgToSend.MyID = (byte)NumOfPlayers.Count;
                                m = msgToSend.ToByte();
                                clientSocket.BeginSend(m, 0, m.Length, SocketFlags.None, new AsyncCallback(OnSend), clientSocket);
                            }
                        }
                        else
                        {
                            inicializaMatriz();
                            msgToSend.ConnStatus = ProtocolMessage.ConnectionStatus.FullRoom;
                        }
                        break;
                    case ProtocolMessage.Command.TextInformation:

                        break;


                    default:
                        break;
                }

                clientSocket.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(MensagemRecebida), clientSocket);
            }
            catch (Exception ex)
            {                
                //Quando o cliente se desconecta gera uma excessão que não vou tratar agora!

                //MessageBox.Show(ex.Message, "Servidor Gomoku", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void inicializaMatriz()
        {
            for (int i = 0; i < size; i++)
                for (int j = 0; j < size; j++)
                    matriz[i, j] = 0;
        }

        //Função que faz varredura em toda a matriz para verififcar se existe um vencedor.
        private ProtocolMessage.WinInform ganhou(int jogador)
        {
            ProtocolMessage.WinInform wi = new ProtocolMessage.WinInform();
            wi.direction = ProtocolMessage.WinDirection.Lost;
            int i, j;
            for (i = 0; i < size - 4; i++)
                for (j = 0; j < size - 4; j++)
                    if (matriz[i, j] == jogador && matriz[i + 1, j + 1] == jogador && matriz[i + 2, j
              + 2] == jogador && matriz[i + 3, j + 3] == jogador && matriz[i + 4, j + 4] == jogador)
                    {
                        wi.x = (byte)j;
                        wi.y = (byte)i;
                        wi.direction = ProtocolMessage.WinDirection.DiagonalToLeft;
                        return wi;
                    }

            for (i = 4; i < size; i++)
                for (j = 0; j < size - 4; j++)
                    if (matriz[i, j] == jogador && matriz[i - 1, j + 1] == jogador && matriz[i - 2, j
              + 2] == jogador && matriz[i - 3, j + 3] == jogador && matriz[i - 4, j + 4] == jogador)
                    {
                        wi.x = (byte)j;
                        wi.y = (byte)i;
                        wi.direction = ProtocolMessage.WinDirection.DiagonalToRight;
                        return wi;
                    }

            for (i = 0; i < size; i++)
                for (j = 4; j < size; j++)
                    if (matriz[i, j] == jogador && matriz[i, j - 1] == jogador && matriz[i, j - 2] == jogador
              && matriz[i, j - 3] == jogador && matriz[i, j - 4] == jogador)
                    {
                        wi.x = (byte)j;
                        wi.y = (byte)i;
                        wi.direction = ProtocolMessage.WinDirection.Vertical;
                        return wi;
                    }

            for (i = 0; i < size - 4; i++)
                for (j = 0; j < size; j++)
                    if (matriz[i, j] == jogador && matriz[i + 1, j] == jogador && matriz[i + 2, j] == jogador &&
              matriz[i + 3, j] == jogador && matriz[i + 4, j] == jogador)
                    {
                        wi.x = (byte)j;
                        wi.y = (byte)i;
                        wi.direction = ProtocolMessage.WinDirection.Horizontal;
                        return wi;
                    }
            return wi;

        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            serverSocket.Close();
        }


    }
}
